package com.yonyougov.devops.cruxcisupport.userserver;

import com.yonyougov.devops.cruxcisupport.server.ServerEntity;
import com.yonyougov.devops.cruxcisupport.threadlocal.ErrorMessageThreadLocal;
import com.yonyougov.devops.cruxcisupport.user.UserEntity;
import com.yonyougov.devops.cruxcisupport.userserver.vo.*;
import com.yonyougov.devops.cruxcisupport.util.SshUtil;
import com.yonyougov.devops.fwc.api.dto.BaseResponseDto;
import com.yonyougov.devops.fwc.api.enums.BaseErrorCode;
import com.yonyougov.devops.fwc.core.dao.impl.BaseDaoImpl;
import io.micrometer.core.instrument.util.StringUtils;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;

import java.io.BufferedReader;
import java.io.StringReader;
import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;

/**
 * t_user_server数据层实现
 */
@Repository
public class UserServerDaoImpl extends BaseDaoImpl<UserServerEntity, BigInteger> implements UserServerDao {
    private final SqlSessionTemplate sqlSessionTemplate;

    public UserServerDaoImpl(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
    }

    @Override
    public List<UserServerPageLikeRespVo> findUserServerPageLike(UserServerPageLikeReqVo userServerPageLikeReqVo) {
        String statement = UserServerEntity.class.getName() + "Mapper.findUserServerPageLike";
        return sqlSessionTemplate.selectList(statement, userServerPageLikeReqVo);
    }

    @Override
    public int countByUserServerLike(UserServerPageLikeReqVo userServerPageLikeReqVo) {
        String statement = UserServerEntity.class.getName() + "Mapper.countByUserServerLike";
        return sqlSessionTemplate.selectOne(statement, userServerPageLikeReqVo);
    }

    @Override
    public void deleteBatchUserServerByIds(UserEntity user, List<BigInteger> ids) {

        String stm = ServerEntity.class.getName() + "Mapper.getServersByUserServerIds";
        List<ServerEntity> serverEntities = sqlSessionTemplate.selectList(stm, ids);

        String st = UserEntity.class.getName() + "Mapper.getPublicKeyByUserId";
        String publicKeys = sqlSessionTemplate.selectOne(st, user.getId());

        serverEntities = userUpdServePublicKey(publicKeys, "", serverEntities);

        if(serverEntities.size()>0){
            for (ServerEntity serverEntity : serverEntities) {
                delete(UserServerEntity.builder().userId(user.getId()).serverId(serverEntity.getId()).build());
            }
        }
    }

    /**
     * 用户更新服务器公钥
     * 默认只更新服务器状态为上线的服务器
     * @param newPublicKey
     * @param serverEntities
     * @return 成功更新的服务器
     */
    public List<ServerEntity> userUpdServePublicKey(String oldPublicKey, String newPublicKey, List<ServerEntity> serverEntities){

        // 命令拼接开始----------------------------------------------------------------------------------------------
        StringJoiner sedE = new StringJoiner("");
        sedE.add(" ([ '1' = '1' ] ");

        // 处理旧的公钥
        oldPublicKey = Optional.ofNullable(oldPublicKey).orElseGet(() -> "39366ece-0cda-48cb-a372-07983642e93e");
        if(!"".equals(oldPublicKey)){
            sedE.add(" && ( [ -f \"/root/.ssh/authorized_keys\" ] && sed -i ");
            for (String s : oldPublicKey.split(",")) {
                sedE.add(String.format(" -e '/%s/d' ", publicKeyEnCode(s.toString())));
            }
            sedE.add(" /root/.ssh/authorized_keys  || [ true ] )");
        }

        // 处理新的公钥
        newPublicKey = Optional.ofNullable(newPublicKey).orElseGet(() -> "");
        if("" != newPublicKey){
            sedE.add(" && echo \"");
            sedE.add(newPublicKey.replaceAll(",","\n").trim());
            System.out.println(newPublicKey.replaceAll(",","\n").trim());
            sedE.add("\" >> /root/.ssh/authorized_keys");
        }

        sedE.add(" ) >> /dev/null && echo true ");
        // 命令拼接完毕----------------------------------------------------------------------------------------------

        System.out.println(sedE.toString());
        List<ServerEntity> successUpdServes = new ArrayList<>();
        List<String> errorServes = new ArrayList<>();
        serverEntities.parallelStream().filter(e->e.getState().equals(1)).forEach(s -> {
            SshUtil sshUtil = new SshUtil();
            String result = null;
            result = sshUtil.exeCmd(s, sedE.toString());
            if(null != result && "true".equals(result)) {
                successUpdServes.add(s);
            }else{
                errorServes.add(String.format("连接服务器超时，server ip = %s",s.getIp()));
            }
        });

        // 处理分配公钥过程中发现的错误
        if(errorServes.size()>0){
            ErrorMessageThreadLocal.setMessage("存在部分服务器更新公钥失败  "+errorServes.stream().reduce((e1,e2)->e1+e2).get());
        }
        return successUpdServes;
    }

    /**
     * 转义特殊字符 （$()*.[]?\^{} 没有+号，+号在公钥中不需要转义
     */
    private String publicKeyEnCode(String str) {
        if(StringUtils.isBlank(str)){
            return str;
        }

        return str.replace("\\", "\\\\").replace("*", "\\*")
                .replace("|", "\\|")
                .replace("{", "\\{").replace("}", "\\}")
                .replace("(", "\\(").replace(")", "\\)")
                .replace("^", "\\^").replace("$", "\\$")
                .replace("[", "\\[").replace("]", "\\]")
                .replace("?", "\\?").replace(",", "\\,")
                .replace("/", "\\/")
                .replace(".", "\\.").replace("&", "\\&");
    }

    @Override
    public BaseResponseDto userAddServer(BigInteger userId, List<BigInteger> serverIds) {
        BaseResponseDto dto = BaseResponseDto.getSuccessInstance();

        String statement = ServerEntity.class.getName() + "Mapper.getServersByIds";
        List<ServerEntity> serverEntities = sqlSessionTemplate.selectList(statement, serverIds);

        String st = UserEntity.class.getName() + "Mapper.getPublicKeyByUserId";
        String publicKeys = sqlSessionTemplate.selectOne(st, userId);

        List<UserServerEntity> successUserServerEntityList = userUpdServePublicKey("", publicKeys, serverEntities).stream().map(e -> UserServerEntity.builder().userId(userId).serverId(e.getId()).build()).collect(Collectors.toList());
        if(ErrorMessageThreadLocal.hasError()){
            dto.setErrorMsg(ErrorMessageThreadLocal.getMessage());
        }
        if (successUserServerEntityList.size() > 0) {
            String s = UserServerEntity.class.getName() + "Mapper.addBatch";
            sqlSessionTemplate.insert(s, successUserServerEntityList);
        }
        return dto;
    }

    @Override
    public int countByUserLinkedServer(ServerLinkedUserPageLikeReqVo reqVo) {
        String statement = UserServerEntity.class.getName() + "Mapper.countByUserLinkedServer";
        return sqlSessionTemplate.selectOne(statement, reqVo);
    }

    @Override
    public List<ServerLinkedUserPageLikeRespVo> findUserLinkedServerPageLike(ServerLinkedUserPageLikeReqVo reqVo) {
        String statement = UserServerEntity.class.getName() + "Mapper.findUserLinkedServerPageLike";
        return sqlSessionTemplate.selectList(statement, reqVo);
    }

    @Override
    public BaseResponseDto findServerPubKey(BigInteger serverId) {
        String statement = ServerEntity.class.getName() + "Mapper.findBy";
        ServerEntity entity = new ServerEntity();
        entity.setId(serverId);
        ServerEntity serverEntity = sqlSessionTemplate.selectOne(statement, entity);

        SshUtil sshUtil = new SshUtil();
        String authKeyResult = sshUtil.exeCmd(serverEntity, "cat " + "/root/.ssh/authorized_keys");

        if (authKeyResult == null) {
            BaseResponseDto dto = BaseResponseDto.getFailInstance(BaseErrorCode.SYS_ERROR);
            dto.setErrorMsg(serverEntity.getIp() + " Connection timed out: connect");
            return dto;
        }

        List<ServerPubKeyRespVo> vos = new LinkedList<>();
        if (StringUtils.isNotBlank(authKeyResult)) {
            try (StringReader reader = new StringReader(authKeyResult);
                 BufferedReader bufferedReader = new BufferedReader(reader)) {
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    String[] sshKeys = line.split(" ");
                    ServerPubKeyRespVo vo = new ServerPubKeyRespVo();
                    if (sshKeys.length == 3) {
                        vo.setName(sshKeys[2]);
                    } else {
                        vo.setName("UnKnow");
                    }
                    vo.setPubKey(line);
                    vos.add(vo);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        BaseResponseDto dto = BaseResponseDto.getSuccessInstance();
        dto.put("data", vos);
        return dto;
    }

    @Override
    public void deleteServerPubKey(ServerDeletePubKeyReqVo pubKeyReqVo) {
        String statement = ServerEntity.class.getName() + "Mapper.findBy";
        ServerEntity entity = new ServerEntity();
        entity.setId(pubKeyReqVo.getServerId());
        ServerEntity serverEntity = sqlSessionTemplate.selectOne(statement, entity);

        SshUtil sshUtil = new SshUtil();
        pubKeyReqVo.getPubKey().forEach(p -> sshUtil.exeCmd(serverEntity, "sed -i -e '/" +
                p.replaceAll("/", "\\\\/") + "/d' /root/.ssh/authorized_keys"));
    }

    @Override
    public void printPubKey() {
        String statement = ServerEntity.class.getName() + "Mapper.getList";
        List<ServerEntity> serverEntities = sqlSessionTemplate.selectList(statement, new ServerEntity());
        serverEntities.forEach(s -> {
            SshUtil sshUtil = new SshUtil();
            String authKeyResult = sshUtil.exeCmd(s, "cat " + "/root/.ssh/authorized_keys");
            System.out.println("************************************");
            System.out.println("主机IP： " + s.getIp() + " 主机名： " + s.getName() + " 备注：" + s.getRemark());
            System.out.println(authKeyResult);
            System.out.println("************************************");
        });
    }

    @Override
    public void updatePubKey(String oldPublicKey, UserEntity userEntity) {
        String statement = ServerEntity.class.getName() + "Mapper.findLinkedServerByUserId";
        List<ServerEntity> serverEntities = sqlSessionTemplate.selectList(statement, userEntity.getId());

        String st = UserEntity.class.getName() + "Mapper.findByPrimaryKey";
        UserEntity user = sqlSessionTemplate.selectOne(st, userEntity.getId());

        Runnable r = () -> serverEntities.forEach(s -> {
            userUpdServePublicKey(oldPublicKey,userEntity.getPublicKey(),serverEntities);
        });
        new Thread(r).start();
    }

    @Override
    public void execCommand(List<BigInteger> serverId, String command) {
        String statement;
        List<ServerEntity> serverEntities;
        if (serverId == null) {
            statement = ServerEntity.class.getName() + "Mapper.getList";
            serverEntities = sqlSessionTemplate.selectList(statement, new ServerEntity());
        } else {
            statement = ServerEntity.class.getName() + "Mapper.getServersByIds";
            serverEntities = sqlSessionTemplate.selectList(statement, serverId);
        }

        Runnable r = () -> serverEntities.forEach(s -> {
            SshUtil sshUtil = new SshUtil();
            sshUtil.exeCmdNoReturn(s, command);
        });
        new Thread(r).start();
    }
}
